﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once

// std headers
#include <string>
#include <map>
#include <memory>

// qt headers
#include <QObject>
#include <QMutex>

// custom headers
#include "ILegend.h"


namespace gmepgcs
{
    class IEditingProjectComponent;
    struct IDetailedProjectComponent;
    struct IParameter;
    class IParameterDefinition;
    struct ISpecification;
    template<typename TData> class IPagedData;
    class ICategoryProperty;
    class FamilyFileService;
    struct IProjectComponent;
    struct IGmepGcsCategory;
}

namespace gmepgcs
{
    class EditingProjectComponentContext : public QObject
    {
        Q_OBJECT
    public:
        /*enum class ModifyState : int32_t
        {
            Invalid =  -1, /// current data can not be saved
            UnChangedAndClean = 0, ///< same as the original, and nothing to update with family service
            UnchangedAndDirty = 1, ///< same as the original, and need to update with family service
            ChangedAndClean = 2, /// different from the original, and nothing to update with family service
            ChangedAndDirty = 3, /// different from the original, and need to update with family service
        };*/

        enum ModifyState
        {
            Invalid     = 0x00000001, //     01
            Valid       = 0x00000002, //     10
            UnChanged   = 0x00000004, //    100
            Changed     = 0x00000008, //   1000
            Clean       = 0x00000010, //  10000
            Dirty       = 0x00000020  // 100000
        };
        Q_ENUM(ModifyState)

    public:
        explicit EditingProjectComponentContext(
            const std::wstring &projectId,
            const std::wstring &enterpriseId,
            const std::wstring &componentId, 
            const int32_t &componentVersion, 
            const std::wstring &componentName,
            QObject *parent = nullptr);

        virtual ~EditingProjectComponentContext();
        
    public:
        std::wstring GetProjectId() const { return m_projectId; };
        std::wstring GetEnterpriseId() const { return m_enterpriseId; };
        std::wstring GetComponentId() const { return m_componentId; };
        int32_t GetComponentVersion() const { return m_componentVersion; };

        std::wstring GetComponentLocalFilePath() const;
        std::wstring GetLegendThumbnailLoalFilePath(const std::wstring &legendId) const;
                
        void Prepare();
        bool Stage(const std::wstring &spec, bool bNeedUpdate = true);
        bool Save(const bool &willUpdateComponentAfter,int& nNum, bool& hasChangeAfterSync, std::wstring& strSuccInfo);

        QString GetActiveComponentName() const;

        std::wstring GetOriginalComponentName() const;

        std::wstring GetActiveCategoryCode() const;
        std::wstring GetActiveCategoryGuid() const;
        std::wstring GetActiveCategoryFullName() const;

        std::vector<std::wstring> GetActiveSpecifications();
        std::wstring GetCurrentSpecificationName() const;

        std::vector<std::shared_ptr<IParameter>> GetActiveGeometyParameters(const std::wstring &specificationName);
        std::vector<std::shared_ptr<IParameter>> GetActiveStandardParameters(const std::wstring &specificationName);
        std::vector<std::shared_ptr<IParameter>> GetActiveCustomParameters(const std::wstring &specificationName);        

        std::map<std::wstring, std::shared_ptr<IParameterDefinition>> GetActiveParameterDefinitions();
        std::shared_ptr<ICategoryProperty> GetActiveCategoryProperty();

        std::vector<std::shared_ptr<IGmepGcsCategory>> GetCategories();

        std::shared_ptr<ILegend> GetActiveLegend(const ILegend::LegendType &legendType);
        
        void SwitchCurrentSpecificationTo(const std::wstring &targetSpecificationName);

    public:
        void ModifyComponentName(const std::wstring &componentName);
        void ModifyComponentCategory(const std::wstring &categoryCode);
        
        void RemoveSpecification(const std::wstring &specificationName);
        void RenameSpecification(const std::wstring specificationOldName, const std::wstring specificationNewName);
        void CopySpecification(const std::wstring &sourceSpecificationName, const std::wstring &targetSpecificationName);
        
        bool ModifyParameterName(const std::wstring parameterOldName, const std::wstring parameterNewName, std::wstring &errorMessage);
        bool ModifyParameterValue(const std::wstring &specificationName, const std::wstring &parameterName, const std::wstring &parameterOldValue, const std::wstring &parameterNewValue);
        bool ModifyParameterUnit(const std::wstring &parameterName, const std::wstring &parameterOldUnit, const std::wstring &parameterNewUnit);
        bool ModifyParameterIsInstance(const std::wstring &parameterName, const bool &parameterOldIsInstance, const bool &parameterNewIsInstance);
        bool ModifyParameterUnit(const std::wstring& sCategoryCode, std::map<std::wstring, std::shared_ptr<IParameterDefinition>>& parameterDefinitions);

        bool AddParameterDefinitions(const std::map<std::wstring, std::shared_ptr<IParameterDefinition>> &parameterDefinitionsToBeAdded);
        bool RemoveParameterDefinitions(const std::map<std::wstring, std::shared_ptr<IParameterDefinition>> &paramterDefintionsToBeRemoved);

        bool ModifyLegend(const ILegend::LegendType &legendType, const std::shared_ptr<ILegend> &oldLegend, const std::shared_ptr<ILegend> &newLegend);

        bool UpdateDrivenParameters(const std::wstring &spec, std::wstring &errorMessage, bool bNeedUpdate = true);
        bool UpdateGeometry(const std::wstring &spec, std::wstring &errorMessage, bool bNeedUpdate = true);

        int GetModifyState();
        
    signals:
        void PrepareFinished(const bool &isReady, const std::wstring errorMessage);
        void StateChanged(int oldState, int newState);
        void CurrentSpecificationChanged(const std::wstring &oldSpec, const std::wstring &newSpec);
        void SpecificationItemsChanged(const std::vector<std::wstring> &specs);
        void SuppressedParametersChanged(const std::vector<std::wstring> &newlySuppressed, const std::vector<std::wstring> &newlyDesuppressed);
        void DrivenParameterUpdateFinish(const bool &success, const bool &hasUpdated);
        void GeometryUpdateFinish(const bool &success);
        void StageFinished(const bool &success);
        void SaveFinished(const bool &success, const std::wstring &errorMessage, std::shared_ptr<IProjectComponent> savedComponent, const bool &willUpdateLoadedComponent);
        void ModifyComponentCategoryFinished(const bool &sucess, const std::wstring &errorMessage);

    private:
        void Init();

        void PrepareDetailedComponent();
        void PrepareCategoryProperty(const std::wstring &categoryCode);
        void PrepareCategories();
        void PrepareSpecifications();
        void PrepareLegends();
        void PrepareComponentFile();
        
        void SetIsComponentDataReay(const bool ready);
        void SetIsCategoryPropertyDataReady(const bool ready);
        void SetIsCategoriesDataReady(const bool ready);
        void SetIsSpecificationsDataReady(const bool ready);
        void SetIsLegendsDataReady(const bool ready);
        void SetIsComponentFileDataReady(const bool ready);
        void CheckIsAllReady();

        bool MountStandardParametersIntoComponent();

        std::vector<std::shared_ptr<IParameter>> GetActiveParametersWithCondition(
            const std::wstring &specificationName, bool(*predicate)(std::shared_ptr<IParameter>));

        void CheckModifyState();
        bool CheckActiveValid();

        void CheckCanDesuppressParameters();
        void SuppressParameters(const std::vector<std::wstring> &parameterNames);

        std::wstring NewTypeGuid();
        
        std::vector<std::shared_ptr<IGmepGcsCategory>> GetFullCategories(std::wstring leafCategoryCode) const;
        
    private:
        std::wstring m_projectId;
        std::wstring m_enterpriseId;
        std::wstring m_componentId;
        int32_t m_componentVersion;
        QString m_componentNameExtension;

        int m_state;
        std::shared_ptr<IEditingProjectComponent> m_spOriginalComponent;
        std::shared_ptr<IEditingProjectComponent> m_spStagedComponent;
        std::shared_ptr<IEditingProjectComponent> m_spActiveComponent;

        std::wstring m_currentSpecificationName;

        std::wstring m_componentEditingFolderPath;
        std::wstring m_componentLocalFilePath;
        std::map<std::wstring, std::wstring> m_mpLegendThumbnailLocalFilePaths;

        bool m_isComponentDataReady;
        bool m_isCategoryPropertyDataReady;
        bool m_isCategoriesDataReady;
        bool m_isSpecificationsDataReady;
        bool m_isLegendsDataReady;
        bool m_isComponentFileDataReady;

        std::map<std::wstring, std::shared_ptr<ICategoryProperty>> m_mpCategoryProperties;

        std::vector<std::shared_ptr<IGmepGcsCategory>> m_vCategories;

        std::map<std::wstring, std::map<std::wstring, std::shared_ptr<ISpecification>>> m_customParametersBinByCategory;
        std::map<std::wstring, std::map<std::wstring, std::shared_ptr<IParameterDefinition>>> m_customParameterDefinitionsBinByCategory;
        
        std::shared_ptr<FamilyFileService> m_spFamilyFileService;
        bool m_lastStageFailed;

    private:
        std::shared_ptr<IDetailedProjectComponent> m_spDetailedProjectComponent;
        std::vector<std::shared_ptr<ISpecification>> m_vSpecifications;
        std::vector<std::shared_ptr<ILegend>> m_vLegends;
        std::shared_ptr<ICategoryProperty> m_spPreparedCategoryProperty;
        std::wstring m_originalComponentName;
    };
}